package summerprojectFX.utilities;

import java.util.ArrayList;
import java.util.StringTokenizer;

public class Transposer {

    private String song, startKey, endKey;
    private char[] keys = {'B', 'C', 'D', 'E', 'F', 'G', 'b', 'c', 'd', 'e',
        'f', 'g'};
    String[] sharpkeys = {"A", "A#", "B", "C", "C#", "D", "D#", "E", "F",
        "F#", "G", "G#", "A", "A#", "B", "C", "C#", "D", "D#", "E", "F",
        "F#", "G", "G#"};
    String[] flatkeys = {"Ab", "A", "Bb", "B", "C", "Db", "D", "Eb", "E", "F",
        "Gb", "G", "Ab", "A", "Bb", "B", "C", "Db", "D", "Eb", "E", "F",
        "Gb", "G"};
    String[] moveKey = {"Ab", "G#", "Bb", "A#", "Db", "C#", "Eb", "D#", "Gb",
        "F#"};
    private String[] types = {"major", "minor", "maj", "min", "add9", "sus4",
        "maj7", "m", "#", "b", "7", "sus2", "sus4", "add2", "add9", "add4",
        "madd2", "madd9", "madd4", "add2add4", "madd2add4", "aug", "dim",
        "dim7", "5", "6", "m6", "6/9", "m6/9", "6/7", "m6/7", "maj6/7",
        "m7", "maj7", "7sus4", "7sus2", "7add4", "m7add4", "9", "m9",
        "maj9", "9sus4", "11", "m11", "maj11", "13", "m13", "maj13",
        "13sus4", "mmaj7", "mmaj9", "7#9", "7b9", "7#5", "7b5", "m7#5",
        "m7b5", "maj7#5", "maj7b5", "9#5", "9b5", "/"};
    private int halfSteps;

    public Transposer(String song, String startKey, String endKey) {
        this.song = song;
        this.startKey = startKey;
        this.endKey = endKey;
        halfSteps = calculateDistance(startKey, endKey);
    }

    public Transposer(String song, String sKey, int halfSteps) {
        this.song = song;
        this.halfSteps = halfSteps;
        if (sKey == null) {
            startKey = "C";
        } else {
            startKey = sKey;
        }

        if (startKey.length() > 1 && startKey.charAt(1) == 'b') {
            int i = 0;
            for (i = 0; i < sharpkeys.length; i++) {
                if (startKey.equalsIgnoreCase(flatkeys[i])) {
                    endKey = flatkeys[i + halfSteps];
                    break;
                }
            }
        } else {
            int i = 0;
            for (i = 0; i < sharpkeys.length; i++) {
                if (startKey.equalsIgnoreCase(sharpkeys[i])) {
                    endKey = sharpkeys[i + halfSteps];
                    break;
                }
            }
        }
    }

    public String getEndKey() {
        return endKey;
    }

    private int calculateDistance(String start, String end) {
        String tempStart = null, tempEnd = null;
        if (start.length() > 1) {
            if (start.charAt(1) == 'b') {
                for (int i = 0; i < moveKey.length; i++) {
                    if (start.equals(moveKey[i])) {
                        tempStart = moveKey[i + 1];
                        break;
                    }
                }
            } else {
                tempStart = start;
            }
        } else {
            tempStart = start;
        }
        if (end.length() > 1) {
            if (end.charAt(1) == 'b') {
                for (int i = 0; i < moveKey.length; i++) {
                    if (end.equals(moveKey[i])) {
                        tempEnd = moveKey[i + 1];
                        break;
                    }
                }
            } else {
                tempEnd = end;
            }
        } else {
            tempEnd = end;
        }

        int s;

        for (s = 0; s < sharpkeys.length; s++) {
            if (sharpkeys[s].equals(tempStart)) {
                break;
            }
        }
        int e;
        for (e = s + 1; e < sharpkeys.length; e++) {
            if (sharpkeys[e].equals(tempEnd)) {
                break;
            }
        }
        return e - s;
    }

    public String transpose() {
        String newSong = "";
        String[] holder = song.split("\\n+");
        ArrayList<String> oneLine;

        for (String s : holder) {
            oneLine = new ArrayList();
            StringTokenizer st = new StringTokenizer(s, "\t ", true);
            while (st.hasMoreTokens()) {
                oneLine.add(st.nextToken());
            }

            StringTokenizer st1 = new StringTokenizer(s, " \t", false);
            ArrayList<String> temp = new ArrayList();

            while (st1.hasMoreTokens()) {

                temp.add(st1.nextToken());

            }

            String[] noWhiteSpace = temp.toArray(new String[temp.size()]);
            if ((noWhiteSpace.length == 1 && (noWhiteSpace[0].charAt(0) == 'A' || noWhiteSpace[0].charAt(0) == 'a')) || isChordLine(noWhiteSpace) == true) {
                s = chordChange(oneLine);
            }
            newSong += s + "\n";
        }
        return newSong;
    }

    private boolean isChordLine(String[] noSpace) {

        for (String s : noSpace) {
            if (isChord(s) == true) {
                return true;
            }
        }
        return false;
    }

    private boolean isChord(String chord) {
        for (char key : keys) {
            if (chord.length() > 0) {
                if (chord.charAt(0) == key) {
                    if (chord.length() == 1) {
                        return true;
                    } else if (chord.length() > 1) {
                        for (String type : types) {
                            if (type.equalsIgnoreCase(chord.substring(1))
                                    || (type.equalsIgnoreCase(chord.substring(2)) && (chord.charAt(1) == 'b' || chord.charAt(1) == '#'))) {
                                return true;
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    private String chordChange(ArrayList<String> oneLine) {
        String rValue = "";
        for (int i = 0; i < oneLine.size(); i++) {
            String temp = oneLine.get(i);
            if (Character.isLetter(temp.charAt(0))) {
                rValue += move(temp);
            } else {
                rValue += temp;
            }
        }
        return rValue;
    }

    private String move(String chord) {
        String root = null;
        if (chord.length() > 1
                && (chord.charAt(1) == '#' || chord.charAt(1) == 'b')) {
            if (chord.charAt(1) == 'b'
                    && (endKey.length() == 1 || (endKey.length() > 1 && endKey.charAt(1) == 'b'))) {
                // if chord is a flat and end key is not sharp, then use
                // flatkeys
                root = chord.substring(0, 2);
                for (int i = 0; i < flatkeys.length; i++) {
                    if (flatkeys[i].equals(root)) {
                        root = flatkeys[i + halfSteps];
                        break;
                    }
                }
            } else if (chord.charAt(1) == 'b' && endKey.charAt(1) == '#') {
                // if chord is a flat and end key is sharp; convert chord to
                // sharp,
                // then use sharpkeys
                root = chord.substring(0, 2);

                for (int i = 0; i < moveKey.length; i++) {
                    if (moveKey[i].equals(root)) {
                        root = moveKey[i + 1];
                        break;
                    }
                }

                for (int i = 0; i < sharpkeys.length; i++) {
                    if (sharpkeys[i].equals(root)) {
                        root = sharpkeys[i + halfSteps];
                        break;
                    }
                }
            } else if (chord.charAt(1) == '#'
                    && (endKey.length() == 1 || (endKey.length() > 1 && endKey.charAt(1) == '#'))) {
                // if chord is a sharp and end key is not flat, use sharpkeys

                root = chord.substring(0, 2);

                for (int i = 0; i < sharpkeys.length; i++) {
                    if (sharpkeys[i].equals(root)) {
                        root = sharpkeys[i + halfSteps];

                        break;
                    }
                }
            } else if (chord.charAt(1) == '#' && endKey.charAt(1) == 'b') {
                // if chord is a sharp and end key is flat, convert to flat and
                // use flatkeys

                root = chord.substring(0, 2);

                for (int i = 0; i < moveKey.length; i++) {
                    if (moveKey[i].equals(root)) {
                        root = moveKey[i - 1];
                        break;
                    }
                }

                for (int i = 0; i < flatkeys.length; i++) {
                    if (flatkeys[i].equals(root)) {
                        root = flatkeys[i + halfSteps];

                        break;
                    }
                }
            }
            chord = root + chord.substring(2);
        } else {
            root = chord.substring(0, 1);
            if (endKey.length() == 1 || endKey.charAt(1) == '#') {
                // chord is not flat or sharp and destination key is not flat
                for (int i = 0; i < sharpkeys.length; i++) {
                    if (sharpkeys[i].equals(root)) {
                        root = sharpkeys[i + halfSteps];

                        break;
                    }
                }
            } else if (endKey.charAt(1) == 'b') {
                // chord is not flat or sharp and destination key is flat
                for (int i = 0; i < flatkeys.length; i++) {
                    if (flatkeys[i].equals(root)) {
                        root = flatkeys[i + halfSteps];

                        break;
                    }
                }
            }
            if (chord.length() > 1) {
                chord = root + chord.substring(1);
            } else {
                chord = root;
            }
        }

        if (chord.length() > 2) {
            if (chord.charAt(1) == '/') {
                return chord.substring(0, 2) + move(chord.substring(2));
            } else if (chord.charAt(2) == '/') {
                return chord.substring(0, 3) + move(chord.substring(3));
            }
        }
        return chord;
    }
}
